package cn.bblocks.common.i18n.config;


import cn.bblocks.common.i18n.I18nHolder;
import cn.bblocks.common.i18n.filter.I18nFilter;
import cn.bblocks.common.i18n.repository.I18nConfigDbLoader;
import cn.bblocks.common.i18n.repository.I18nLocaleHolder;
import cn.bblocks.common.i18n.repository.I18nMessageResource;
import cn.bblocks.common.i18n.interceptor.I18nInterceptor;
import cn.bblocks.common.i18n.repository.SimpleI18NConfigDbLoaderImpl;
import cn.bblocks.common.i18n.task.I18NConfigDbLoaderTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Locale;
import java.util.concurrent.Executor;

/**
 * 配置文件
 */
@Configuration
@EnableConfigurationProperties({I18nProperties.class})
@Slf4j
@Import({DataSourceConfig.class})
public class I18nAutoConfig {

    @Bean
    public I18nHolder getI18nUtil(I18nMessageResource messageSource,I18nLocaleHolder i18nLocaleHolder, I18nProperties i18NProperties) {
        // 不论是否启用都会配置，保证这个工具类不会报错
        return i18NProperties.getEnable() ? new I18nHolder(messageSource, i18nLocaleHolder) : new I18nHolder();
    }

    @ConditionalOnProperty(prefix = "spring.ex.i18n", name = "enable", havingValue = "true")
    @Configuration
    static class I18nFilterConfig {

        @Resource
        private I18nLocaleHolder i18nLocaleHolder;
        @Resource
        private I18nProperties i18nProperties;

        @Bean
        public I18nFilter i18nFilter() {
            I18nFilter i18nFilter = new I18nFilter();
            I18nInterceptor interceptor = new I18nInterceptor();
            interceptor.setI18nLocaleHolder(i18nLocaleHolder);
            interceptor.setI18nProperties(i18nProperties);

            i18nFilter.setI18nInterceptor(interceptor);
            return i18nFilter;
        }
    }

    @ConditionalOnProperty(prefix = "spring.ex.i18n", name = "enable", havingValue = "true")
    @Configuration
    @EnableCaching
    @EnableScheduling
    //@ComponentScan("cn.bblocks.common.i18n")
    static class I18nResourceConfig {

        /**
         * 采用默认的配置文件配置 messages开头的文件，编码为utf8<br>
         * 如 messages_zh_CN.properties ,  messages_en_US.properties
         *
         * @return {@link MessageSourceProperties}
         */
        @ConditionalOnMissingBean(MessageSourceAutoConfiguration.class)
        @Bean
        public MessageSourceProperties messageSourceProperties() {
            return new MessageSourceProperties();
        }

        @ConditionalOnMissingBean(MessageSourceAutoConfiguration.class)
        @Bean
        public ResourceBundleMessageSource initResourceBundleMessageSource(MessageSourceProperties messageSourceProperties) {
            ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
            resourceBundleMessageSource.setBasename(messageSourceProperties.getBasename());
            resourceBundleMessageSource.setDefaultEncoding(messageSourceProperties.getEncoding().name());
            return resourceBundleMessageSource;
        }

        @Primary
        @Bean
        public I18nMessageResource initMessageResource(ResourceBundleMessageSource resourceBundleMessageSource,
                                                       I18nLocaleHolder i18NLocaleSettings) {
            I18nMessageResource i18nMessageResource = new I18nMessageResource(i18NLocaleSettings.getDefaultLocale());
            i18nMessageResource.setParentMessageSource(resourceBundleMessageSource);
            return i18nMessageResource;
        }

        @Bean
        public I18nLocaleHolder getI18nLocaleSetting(I18nProperties i18nProperties) {
            Locale locale;
            try {
                locale = new Locale.Builder()
                        .setLanguageTag(i18nProperties.getDefaultLocale().replace("_", "-").toLowerCase())
                        .build();
            } catch (Exception e) {
                log.error("解析默认语言时出现错误, setting = {}", i18nProperties.getDefaultLocale(), e);
                throw new IllegalArgumentException("解析默认语言时出现错误，请查看日志");
            }
            return new I18nLocaleHolder(locale);
        }


        @Bean(name = "i18nJdbcTemplate")
        @ConditionalOnMissingBean(name = "i18nJdbcTemplate")
        public JdbcTemplate getJdbcTemplate(@Qualifier("i18nDataSource") DataSource i18nDataSource) {
            try {
                if (i18nDataSource == null) {
                    log.error("未配置国家化数据源,请使用@Bean构造一个名为i18nDataSource的DataSource或者直接重新此方法");
                }
                return new JdbcTemplate(i18nDataSource);
            } catch (BeansException e) {
                log.error("无效的数据源{}", i18nDataSource, e);
                throw new IllegalArgumentException("创建数据源时出现错误，请查看日志");
            }
        }

        @Bean(name = "defaultI18nDataLoadService")
        public I18nConfigDbLoader getI18nDataLoadService(I18nProperties i18nProperties,
                                                        JdbcTemplate i18nJdbcTemplate) {
            return new SimpleI18NConfigDbLoaderImpl(i18nProperties.getConfigTable(), i18nJdbcTemplate);
        }

        @ConditionalOnProperty(name = "spring.ex.i18n.cron",  matchIfMissing = false)
        @Bean(name = "i18NConfigDbLoaderTask")
        public I18NConfigDbLoaderTask i18NConfigDbLoaderTask(I18nProperties i18nProperties) {
            log.info("启动定时同步i18n资源任务:{}",i18nProperties.getCron());
            return new I18NConfigDbLoaderTask();
        }


        /**
         * 线程池配置，主要是异步用
         */
        @ConditionalOnProperty(prefix = "spring.ex.i18n", name = "mark", havingValue = "true")
        @Configuration
        @EnableAsync
        static class I18nInvalidMarkerConfig {

            @Bean("i18nExecutor")
            @Autowired
            public Executor getAsyncExecutor(I18nProperties i18NProperties) {
                ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
                executor.setCorePoolSize(1);
                executor.setMaxPoolSize(2);
                executor.setQueueCapacity(i18NProperties.getMarkPoolSize());
                executor.setThreadNamePrefix("i18n-executor-");
                executor.initialize();
                return executor;
            }

            @Bean
            public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
                return new SimpleAsyncUncaughtExceptionHandler();
            }
        }
    }
}
